#include <iostream>
#include <string>
#include <fstream>
#include <map>
#include <vector>
#include "pin.H"

#define SKIP_INS 5000000
#define MAX_SKIP_INS 10000000
// The running count of instructions is kept here
// make it static to help the compiler optimize docount
static UINT64 icount = 0, ucount = 0;

static map <string, INS> ins_data_map;

VOID RecordMemRead(void *iaddr, void *raddr)
{
	if(icount < SKIP_INS)return;
	cout << "I:" << iaddr << " R:" << raddr << endl;
}

VOID RecordMemWrite(void *iaddr, void *waddr)
{
	if(icount < SKIP_INS)return;
	cout << "I:" << iaddr << " W:" << waddr << endl;
}

string type(string check)
{
	if(check == "NOP")return "NOP";
	if(check == "ADD" || check == "ADC" || check == "SUB" || check == "SBB" ||
		check == "INC" || check == "DEC" || check == "AND" || check == "OR" ||
		check == "XOR" || check == "TEST" || check == "CMP" || check == "NEG" ||
		check == "NOT" || check == "JMP" || check == "CALL" || check == "JB" ||
		check == "LOOPW" || check == "LOOPD" || check == "RET" ||check == "RETN" || 
		check == "RETF") return "ADDER";
	if(check == "DIV" || check == "IDIV" || check == "FDIV") return "DIVIDER";
	if(check == "MUL" || check == "IMUL" || check == "FMUL") return "MULTIPLIER";
	if(check=="SHR" || check=="SHL" || check=="SAR" || check=="SAL" || check=="ROR" || check=="ROL" || check=="SHLD" || check=="SHRD" ||
		check=="RCR" || check=="RCL" || check=="BT" ||	check=="BTR" ||	check=="BTC" || check=="BTS" || check=="BSF" || check=="BSR")return "LOGICAL";

	if (check =="MOV" || check =="XCHG" || check =="PUSH" || check =="POP" ||
		check =="PUSHF" || check =="POPF" || check =="LEA" || check =="LDS" ||
		check =="LES" || check =="LFS" || check =="LGS" || check =="LSS" || check =="FLD" ||
		check =="FST") return "LOADSTORE";
	return "DEFAULT";
}

// This function is called before every instruction is executed
VOID docount_print(ADDRINT o) 
{ 
	icount++; 
	if(icount <  SKIP_INS)return;
        if(icount >= MAX_SKIP_INS) exit(0);
	string s = StringFromAddrint(o);
	INS ins = ins_data_map[s];
	cout << "INSAddress::" << s << " " << INS_Mnemonic(ins) << " " << type(INS_Mnemonic(ins)) << " " ;
	cout << "ReadRegMax::" << INS_MaxNumRRegs(ins) << " ";
	for(UINT32 i = 0; i < INS_MaxNumRRegs(ins); i++)
	{
		cout << INS_RegR(ins, i) << " ";
	}
	cout << "WriteRegMax::" << INS_MaxNumWRegs(ins) << " ";
	for(UINT32 i = 0; i < INS_MaxNumWRegs(ins); i++)
		cout << INS_RegW(ins, i) << " ";
	cout << "MemOpCount::" << INS_MemoryOperandCount(ins) << " ";
	cout << "Disassemble::" << INS_Disassemble(ins) ;
	cout << endl;
}
    

// Pin calls this function every time a new instruction is encountered
VOID Instruction(INS ins, VOID *v)
{
    ins_data_map[StringFromAddrint(INS_Address(ins))] = ins; 
    // Insert a call to docount before every instruction, no arguments are passed
    INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount_print, IARG_ADDRINT, INS_Address(ins), IARG_END);
    UINT32 memOperands = INS_MemoryOperandCount(ins);
    for(UINT32 memOp = 0; memOp < memOperands; memOp++)
    {
        if(INS_MemoryOperandIsRead(ins, memOp))
        {
                INS_InsertPredicatedCall(
                ins, IPOINT_BEFORE, (AFUNPTR)RecordMemRead,
		IARG_INST_PTR,
                IARG_MEMORYOP_EA, memOp,
                IARG_END);
        }
        if(INS_MemoryOperandIsWritten(ins, memOp))
        {
		INS_InsertPredicatedCall( ins, IPOINT_BEFORE, (AFUNPTR)RecordMemWrite, IARG_INST_PTR, IARG_MEMORYOP_EA, memOp, IARG_END);
        }
    }
}

KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool",
    "o", "inscount.out", "specify output file name");

// This function is called when the application exits
VOID Fini(INT32 code, VOID *v)
{
    // Write to a file since cout and cerr maybe closed by the application
    ofstream OutFile;
    OutFile.open(KnobOutputFile.Value().c_str());
    OutFile.setf(ios::showbase);
    OutFile << "Count " << icount << " :: " << ucount << endl;
    OutFile.close();
}

/* ===================================================================== */
/* Print Help Message                                                    */
/* ===================================================================== */

INT32 Usage()
{
    cerr << "This tool counts the number of dynamic instructions executed" << endl;
    cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
    return -1;
}

/* ===================================================================== */
/* Main                                                                  */
/* ===================================================================== */
/*   argc, argv are the entire command line: pin -t <toolname> -- ...    */
/* ===================================================================== */

int main(int argc, char * argv[])
{
    // Initialize pin
    if (PIN_Init(argc, argv)) return Usage();

    // Register Instruction to be called to instrument instructions
    INS_AddInstrumentFunction(Instruction, 0);

    // Register Fini to be called when the application exits
    PIN_AddFiniFunction(Fini, 0);
    
    // Start the program, never returns
    PIN_StartProgram();
    return 0;
}
